/*
	The Raja Toolkit.
	Copyright (C) 2011 Raja-Toolkit Foundation

	This library is free software; you can redistribute it and/or
	modify it under the terms of the GNU Lesser General Public
	License as published by the Free Software Foundation; either
	version 3.0 of the License, or (at your option) any later version.

	This library is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public
	License along with this library; if not, write to the Free Software
	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include "primitives.hpp"

namespace raja
{
namespace math
{

///////////////////////////////////////////////////////////////////////////////
/// plane
///

///
/// @brief normalize the plane
/// @param p the plane to normalize
/// @return normalized plane
///
template<typename T>
tplane<T>		normalize(const tplane<T>& p)
{
	T			l	= length(p.normal());

	return tplane<T>(p.normal() / l, p.d * l);
}

///////////////////////////////////////////////////////////////////////////////
template tplane<real32>	normalize<real32>(const tplane<real32>& p);
template tplane<real64>	normalize<real64>(const tplane<real64>& p);
///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
/// tri2
///

///
/// @brief get barycentric coordinates
/// @param p the input point
/// @return corresponding barycentric coordinates (u, v)
///
template<typename T>
tvec2<T>	ttri2<T>::operator[](const tvec2<T>& p) const
{
	tvec2<T>	a, b, c, res;

	// center around v0
	a	= v[1] - v[0];
	b	= v[2] - v[0];
	c	= p - v[0];

	// now a and b makes the new coordinate system
	res	= inverse(tmat2<T>(a, b)) * c;

	return res;
}

///
/// @brief convert from barycentric coordinates to system coordinates
/// @param p input barycentric coordinates
/// @return corresponding system coordinates
///
template<typename T>
tvec2<T>	ttri2<T>::convert_barycentric_coord(const tvec2<T>& p) const
{
	tvec2<T>	a, b, res;

	// center around v0
	a	= v[1] - v[0];
	b	= v[2] - v[0];

	// now a and b makes the new coordinate system
	res	= (tmat2<T>(a, b) * p) + v[0];

	return res;
}

///
/// @brief get the area of the triangle using heron's formula
/// @return area of the triangle
///
template<typename T>
T			ttri2<T>::area() const
{
	tvec2<T>	a, b, c;
	a	= v[1] - v[0];
	b	= v[2] - v[1];
	c	= v[0] - v[2];

	T			la, lb, lc, p;
	la	= length(a);
	lb	= length(b);
	lc	= length(c);

	p	= half(la + lb + lc);

	return std::sqrt(p * (p - la) * (p - lb) * (p - lc));
}

///////////////////////////////////////////////////////////////////////////////
template struct ttri2<real32>;
template struct ttri2<real64>;
///////////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////
/// tri3
///

///
/// @brief get barycentric coordinates
/// @param p the input point
/// @return corresponding barycentric coordinates (u, v)
///
template<typename T>
tvec3<T>	ttri3<T>::operator[](const tvec3<T>& p) const
{
	tvec3<T>	a, b, c, d, res;

	// center around v0
	a	= v[1] - v[0];
	b	= v[2] - v[0];
	c	= cross(v[0], v[1]);	// and get a 3rd axis
	d	= p - v[0];

	// now a and b makes the new coordinate system
	res	= inverse(tmat3<T>(a, b, c)) * d;

	return res;
}

///
/// @brief convert from barycentric coordinates to system coordinates
/// @param p input barycentric coordinates
/// @return corresponding system coordinates
///
template<typename T>
tvec3<T>	ttri3<T>::convert_barycentric_coord(const tvec3<T>& p) const
{
	tvec3<T>	a, b, c, res;

	// center around v0
	a	= v[1] - v[0];
	b	= v[2] - v[0];
	c	= cross(v[0], v[1]);	// and get a 3rd axis

	// now a and b makes the new coordinate system
	res	= (tmat3<T>(a, b, c) * p) + v[0];

	return res;
}

///
/// @brief get the area of the triangle using heron's formula
/// @return area of the triangle
///
template<typename T>
T			ttri3<T>::area() const
{
	tvec3<T>	a, b, c;
	a	= v[1] - v[0];
	b	= v[2] - v[1];
	c	= v[0] - v[2];

	T			la, lb, lc, p;
	la	= length(a);
	lb	= length(b);
	lc	= length(c);

	p	= half(la + lb + lc);

	return std::sqrt(p * (p - la) * (p - lb) * (p - lc));
}

///////////////////////////////////////////////////////////////////////////////
template struct ttri3<real32>;
template struct ttri3<real64>;
///////////////////////////////////////////////////////////////////////////////

}	// namespace math
}	// namespace raja
